#include "StorageInterface.h"
#include "DBInterface.h"

bool StorageInterface::S3Copy(const std::string& cmd)
{
	for(int i = 0 ; i < Settings::GetSettings()->S3_RETRY_COUNT ; i ++)
	{
		if(_SUCCESS == PROCESS::Execute2(_EXE_FILE , cmd))
			return true;

		sleep(2); 
	}

	LOG::PushProcessLog(_ERROR , std::string("failed to copy file : ") + cmd);
	printf("failed to copy file : %s\n" , cmd.c_str() );

	return false;
}

bool StorageInterface::CopyFile(const std::string& src_path , const std::string& dest_path)
{
	std::string cmd = "aws s3 cp " + src_path + " " + dest_path;
	return S3Copy(cmd);
}

bool StorageInterface::CopyTsFiles(const std::string& job_id , const std::string& src_path , const std::string& dest_path)
{
	std::string cmd = "aws s3 cp " + src_path + " " + dest_path + " --recursive --content-type \"video/MP2T\" --exclude \"*\" --include \""+job_id+"*.ts\"";
	return S3Copy(cmd);
}

bool StorageInterface::CopyM3u8Files(const std::string& job_id , const std::string& src_path , const std::string& dest_path)
{
	std::string cmd = "aws s3 cp " + src_path + " " + dest_path + " --recursive --content-type \"application/x-mpegURL\" --exclude \"*\" --include \""+job_id+"*.m3u8\"";
	return S3Copy(cmd);
}


bool StorageInterface::LaunchDefaultAgents()
{
	std::vector<std::string> vecDefaultInstance;

	_DB_RESULT db_result = DBInterface::GetRegInstanceList(Settings::GetSettings()->RENDERER_ID , vecDefaultInstance , true);
	if(_DB_SUCCESS != db_result)
	{
		char buf[1024];
		sprintf(buf , "DB::GetRegInstanceList(always)  :%d" ,db_result);
		printf("%s\n" , buf);
		LOG::PushProcessLog(_ERROR, buf);

		return false;
	}
	else if(vecDefaultInstance.size() > 0)
	{
		std::vector<std::string> vecStoppedInstance;

		int stopped_count = GetStoppedInstances(vecDefaultInstance ,vecDefaultInstance.size() , vecStoppedInstance);
		if(stopped_count < 0)
			return false;

		if(stopped_count > 0)
		{

			if(Execute(_START ,vecStoppedInstance))
			{
				for(int i = 0 ; i < vecStoppedInstance.size() ; i ++)
				{
					LOG::PushEC2Log(_REQUIRED , vecStoppedInstance[i] , true); 
				}
			}
			else
			{
				return false;
			}
		}
	}

	return true;

}

bool StorageInterface::StartEC2(const int count)
{
	if(count == 0)
		return true;

	std::vector<std::string> vecRegInstance;
	_DB_RESULT db_result = DBInterface::GetRegInstanceList(Settings::GetSettings()->RENDERER_ID , vecRegInstance);
	if(_DB_SUCCESS != db_result)
	{
		char buf[1024];
		sprintf(buf , "DB::GetRegInstanceList  :%d" ,db_result);
		printf("%s\n" , buf);
		LOG::PushProcessLog(_ERROR, buf);

		return false;
	}
	else if(vecRegInstance.size() > 0)
	{
		std::vector<std::string> vecStoppedInstance;
		int stopped_count = GetStoppedInstances(vecRegInstance , count , vecStoppedInstance);
		if(stopped_count < 0)
			return false;

		if(stopped_count > 0)
		{
			if(Execute(_START ,vecStoppedInstance))
			{
				for(int i = 0 ; i < vecStoppedInstance.size() ; i ++)
				{
					LOG::PushEC2Log(_REQUIRED , vecStoppedInstance[i] , true); 
				}
			}
			else
			{
				return false;
			}
		}
	}

	return true;
}

bool StorageInterface::StopEC2()
{
	std::vector<std::string> vecStoppedAgentInstance;

	_DB_RESULT db_result = DBInterface::GetStoppedAgentInstanceList(Settings::GetSettings()->RENDERER_ID , vecStoppedAgentInstance);
	if(_DB_SUCCESS != db_result)
	{
		char buf[1024];
		sprintf(buf , "DB::GetStoppedAgentInstanceList  :%d" ,db_result);
		printf("%s\n" , buf);
		LOG::PushProcessLog(_ERROR, buf);

		return false;
	}
	else if(vecStoppedAgentInstance.size() > 0)
	{
		std::vector<std::string> vecRunningInstance;
		int running_count = GetRunningInstances(vecStoppedAgentInstance ,vecRunningInstance);
		if(running_count < 0)
			return false;

		if(running_count > 0)
		{		
			if(Execute(_STOP , vecRunningInstance))
			{
					for(int i = 0 ; i < vecRunningInstance.size() ; i ++)
					{
						LOG::PushEC2Log(_REQUIRED , vecRunningInstance[i] , false); 
					}
			}
			else
			{
				return false;
			}
		}
	}

	return true;
}

bool StorageInterface::Execute(const _EC2_EXE_CMD_TYPE& type , const std::vector<std::string>& vecEC2)
{
	if(vecEC2.size() == 0)
		return true;

	std::string cmd_header;

	switch(type)
	{
	case _START:
		cmd_header = "aws ec2 start-instances --instance-ids ";
		break;
	case _STOP:
		cmd_header = "aws ec2 stop-instances --instance-ids ";
		break;
	default:
		return false;
	}


	std::vector<string_vector> vv;
	MATH::DivideVectorBySize(ID_LIST_LIMIT , vecEC2 , vv);

	for(int i = 0 ; i < vv.size() ; i ++)
	{
		std::string id_list = "";

		for(int k = 0 ; k < vv[i].size() ; k ++)
		{
			id_list += vv[i][k] + " ";
		}

		std::string cmd = cmd_header + id_list;



		if(_SUCCESS != PROCESS::Execute(_EXE_FILE , cmd))
			return false;
	}

	return true;

}

bool StorageInterface::GetInfo(const _EC2_INFO_CMD_TYPE& type , const std::vector<std::string>& vecEC2 , std::string& outputString)
{
	if(vecEC2.size() == 0)
		return true;

	std::string cmd_header;

	switch(type)
	{
	case _GET_RUNNING_LIST:
		cmd_header = "aws ec2 describe-instances --output json --filters Name=instance-state-name,Values=running --instance-ids "; 
		break;
	case _GET_RUNNING_COUNT:
		cmd_header = "aws ec2 describe-instance-status --output json --instance-ids "; 
		break;
	case _GET_STOPPED_LIST:
		cmd_header = "aws ec2 describe-instance-status --output json --include-all-instances --filters Name=instance-state-name,Values=stopped --instance-ids "; 
		break;
	default:
		return false;
	}

	std::string id_list = "";

	for(int k = 0 ; k < vecEC2.size() ; k ++)
	{
		id_list += vecEC2[k] + " ";
	}

	std::string cmd = cmd_header + id_list;
	return _SUCCESS == PROCESS::Execute(cmd , outputString);

}

int StorageInterface::GetRunningInstances(const std::vector<std::string>& vecEC2 , std::vector<std::string>& vecRunning) 
{
	vecRunning.clear();

	std::vector<string_vector> vv;
	MATH::DivideVectorBySize(ID_LIST_LIMIT , vecEC2 , vv);

	for(int i = 0 ; i < vv.size() ; i ++)
	{

		std::string json_str;
		
		if(!GetInfo(_GET_RUNNING_LIST , vv[i] , json_str))
			return -1;

		if(json_str.length() > 0)
		{
			Json::Value root;
			if(PACKET_BASE::GetRoot(json_str , root))
			{


				for(int r = 0 ; r < root["Reservations"].size() ; r ++)
				{
					for(int i = 0 ; i < root["Reservations"][r]["Instances"].size() ; i ++)
					{
						Json::Value& instance = root["Reservations"][r]["Instances"][i];

						std::string id = PACKET_BASE::GetStringS(instance , "InstanceId");

						vecRunning.push_back(id);


					}

				}

			} 

		} 

	} 

	
	return vecRunning.size();
}

int StorageInterface::GetRunningInstanceIPs(const std::vector<std::string>& vecEC2 , const int limit_min, const int count ,std::vector<std::string>& vecRunning) 
{
	vecRunning.clear();

	std::priority_queue< EC2_INSTANCE, std::vector<EC2_INSTANCE> > q;


	std::vector<string_vector> vv;
	MATH::DivideVectorBySize(ID_LIST_LIMIT , vecEC2 , vv);

	for(int i = 0 ; i < vv.size() ; i ++)
	{
		std::string json_str;
	
		if(!GetInfo(_GET_RUNNING_LIST , vv[i] , json_str))
			return -1;

		if(json_str.length() > 0)
		{
			Json::Value root;
			if(PACKET_BASE::GetRoot(json_str , root))
			{
				for(int r = 0 ; r < root["Reservations"].size() ; r ++)
				{
					for(int i = 0 ; i < root["Reservations"][r]["Instances"].size() ; i ++)
					{
						Json::Value& instance = root["Reservations"][r]["Instances"][i];

						std::string ip = PACKET_BASE::GetStringS(instance , "PrivateIpAddress");
						std::string launch_time = PACKET_BASE::GetStringS(instance , "LaunchTime");

						if(ip.length() > 0 && launch_time.length() > 0)
						{
							EC2_INSTANCE ec2(ip , launch_time , limit_min); 
							q.push(ec2); 
						}
					}

				}

			} 

		} 

	} 



	while(q.size() > 0)
	{

		vecRunning.push_back(q.top().id);
		q.pop();
		if(vecRunning.size() == count)
			break;
	}
	
	return vecRunning.size();
}


int StorageInterface::GetStoppedInstances(const std::vector<std::string>& vecEC2, const int count, std::vector<std::string>& vecStopped)
{
	vecStopped.clear();

	std::vector<string_vector> vv;
	MATH::DivideVectorBySize(ID_LIST_LIMIT , vecEC2 , vv);

	for(int i = 0 ; i < vv.size() ; i ++)
	{
		std::string json_str;
		
		if(!GetInfo(_GET_STOPPED_LIST , vv[i] , json_str))
			return -1;

		if(json_str.length() > 0)
		{
			Json::Value root;
			if(PACKET_BASE::GetRoot(json_str , root))
			{
				for(int i = 0 ; i < root["InstanceStatuses"].size() ; i ++)
				{
					Json::Value& status = root["InstanceStatuses"][i];

					std::string instance_id = PACKET_BASE::GetStringS(status , "InstanceId");

					vecStopped.push_back(instance_id);
					if(vecStopped.size() == count)
					{
						return count;
					}

				}

			} 

		} 

	} 

	return vecStopped.size();
}


bool StorageInterface::StopAgent(const unsigned int limit_minute , const unsigned int count)
{
	if(count == 0)
		return true;

	std::vector<std::string> vecRunningAgent;
	_DB_RESULT db_result = DBInterface::GetRunningAgentInstanceList(Settings::GetSettings()->RENDERER_ID , vecRunningAgent);
	if(_DB_SUCCESS != db_result)
	{
		char buf[1024];
		sprintf(buf , "DB::GetRunningAgentInstanceList  :%d" ,db_result);
		printf("%s\n" , buf);
		LOG::PushProcessLog(_ERROR, buf);

		return false;
	}


	const int to_stop_agent = count > vecRunningAgent.size() ? vecRunningAgent.size() : count;

	if(to_stop_agent > 0)
	{
		std::vector<std::string> vecIP;
		int running_count = GetRunningInstanceIPs(vecRunningAgent , limit_minute , to_stop_agent , vecIP);
		if(running_count < 0)
			return false;

		if(running_count > 0)
		{
			return _DB_SUCCESS == DBInterface::SetStoppingAgents(Settings::GetSettings()->RENDERER_ID , vecIP);



		}
	}

	return true;
	
}

int StorageInterface::GetRunningInstanceCount()
{
	std::vector<std::string> vecRegInstance;

	if(DBInterface::GetRegInstanceList(Settings::GetSettings()->RENDERER_ID , vecRegInstance) != _DB_SUCCESS)
		return -1;

	int count = 0;

	std::vector<string_vector> vv;
	MATH::DivideVectorBySize(ID_LIST_LIMIT , vecRegInstance , vv);

	for(int i = 0 ; i < vv.size() ; i ++)
	{

		std::string json_str;
		if(!GetInfo(_GET_RUNNING_COUNT , vv[i] , json_str))
			return -1;

		if(json_str.length() > 0)
		{
			Json::Value root;
			if(PACKET_BASE::GetRoot(json_str , root))
			{
				count += root["InstanceStatuses"].size();
			}
		}

	} 


	return count;
}



bool StorageInterface::CopyOutput(_JOB_INFO& job_info)
{

	std::string high_movie = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final" + OUT_EXT;
	if(!StorageInterface::CopyFile(high_movie , job_info.GetResultHdVideoFilePath()) )
		return false;

	std::string middle_movie = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final_middle" + OUT_EXT;
	if(!StorageInterface::CopyFile(middle_movie , job_info.GetResultMdVideoFilePath()) )
		return false;

	if(job_info.HasLowResolution())
	{
		std::string low_movie = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_final_low" + OUT_EXT;
		if(!StorageInterface::CopyFile(low_movie , job_info.GetResultSdVideoFilePath()) )
			return false;
	}

	if(!StorageInterface::CopyTsFiles(job_info.GetJobId() , Settings::GetSettings()->TMP_DIR , job_info.GetResultHlsPath()) )
		return false;

	if(!StorageInterface::CopyM3u8Files(job_info.GetJobId() , Settings::GetSettings()->TMP_DIR , job_info.GetResultHlsPath()) )
		return false;

	std::string web_img = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_web" + THUMBNAIL_EXT;
	if(!StorageInterface::CopyFile(web_img , job_info.GetResultWebThumbFilePath()) )
		return false;

	std::string mobile_img = Settings::GetSettings()->TMP_DIR + job_info.GetJobId() + "_mobile" + THUMBNAIL_EXT;
	if(!StorageInterface::CopyFile(mobile_img , job_info.GetResultMobileThumbFilePath()) )
		return false;


	return true;

}
